/*
 * @Author: xiaosihan
 * @Date: 2022-08-20 14:50:15
 * @Last Modified by: xiehangyun
 * @Last Modified time: 2025-02-27 18:29:08
 */
import utils from '@/utils/utils';
import { uniqBy } from 'lodash';
import { autorun } from 'mobx';
import {
  AmbientLight,
  BackSide,
  Color,
  DirectionalLight,
  FrontSide,
  Group,
  Mesh,
  MeshBasicMaterial,
  MeshStandardMaterial,
  OrthographicCamera,
  PointLight,
  SRGBColorSpace,
  Vector3,
} from 'three';
import ThreeBase from 'three-base';
import threeUtils from 'three-base/threeUtils';
import MockupMesh from './MockupMesh/MockupMesh';
import editorStore from './editorStore';
import textureManager from './textureManager';

class Preview2DRenderer extends ThreeBase {
  constructor() {
    super();
    window.preview2DRenderer = this;
    this.setCameraType('orthographicCamera');
  }

  // 环境光
  ambientLight = (() => {
    const ambientLight = new AmbientLight('#fff', 1);
    this.scene.add(ambientLight);
    return ambientLight;
  })();

  //点光源1
  pointLight1 = (() => {
    const pointLight1 = new PointLight('#fff', 1.2);
    pointLight1.distance = 1000;
    pointLight1.decay = 0;
    pointLight1.position.set(10, 10, 10);
    this.scene.add(pointLight1);
    return pointLight1;
  })();

  // 方向光
  directionalLight = (() => {
    const directionalLight = new DirectionalLight('#fff', 1);
    directionalLight.position.set(-10, 10, 5);
    directionalLight.target.position.set(0, 0, 0);
    this.scene.add(directionalLight);
    return directionalLight;
  })();

  dispose = autorun(
    () => {
      this.needRender = editorStore.needUpdateTime > 0;
    },
    { delay: 30 },
  );

  setSize(width: number, height: number): void {
    super.setSize(width, height);
    Object.assign(this.orthographicCamera, {
      left: -5,
      bottom: -5,
      top: 5,
      right: 5,
    });
    this.orthographicCamera.updateProjectionMatrix();
  }

  async init() {
    super.init();
    // this.scene.background = new Color('#F5F5F5'); // 渲染上身效果不要背景
    this.axesHelper.visible = false;
    this.gridHelper.visible = false;
    this.renderer.outputColorSpace = SRGBColorSpace;
    this.orthographicCamera.far = 5010;
    this.orthographicCamera.near = 4990;
    this.orthographicCamera.updateProjectionMatrix();
    if (utils.isProd) {
      this.controls.rotateSpeed = 0;
      this.controls.enablePan = false;
    }
    this.autoRender = false;
    this.resetLookAt({
      center: new Vector3(0, 0, 0),
      position: new Vector3(0, 0, 10),
    });
  }

  // 画布对象
  canvasMap: Array<{ url: string; canvas: HTMLCanvasElement }> = [];

  // 上身效果模型组
  mockupMeshGroup = (() => {
    const mockupMeshGroup = new Group();
    this.scene.add(mockupMeshGroup);
    return mockupMeshGroup;
  })();

  clear() {
    this.mockupMeshGroup.clear();
    this.canvasMap = [];
  }

  // 添加一个上身效果模型
  async addMockupMesh(mockup: string, canvas: HTMLCanvasElement, color: `#${string}` = '#ffffff') {
    // 删除没有在页面上的canvas
    this.canvasMap.push({ url: mockup, canvas });
    this.canvasMap = uniqBy(
      this.canvasMap.filter((c) => threeUtils.inDocument(c.canvas)),
      'canvas',
    );

    if (!this.mockupMeshGroup.children.some((obj) => obj.name === mockup)) {
      const mockupMesh = new MockupMesh(mockup);
      this.mockupMeshGroup.add(mockupMesh);
      const { goodsData } = editorStore;

      // 等待加载完成
      await new Promise<void>((resolve, reject) => {
        mockupMesh.addEventListener('loaded', () => {
          const backMeshs: Array<Mesh> = [];

          // 初始衣服的颜色
          const clothColor = new Color(color);
          const max = Math.max(...clothColor.toArray());
          const colorInterval = 1 - max;
          clothColor.r += colorInterval * 0.01;
          clothColor.g += colorInterval * 0.01;
          clothColor.b += colorInterval * 0.01;

          mockupMesh.traverseMesh((mesh) => {
            // 2d 上身效果模型原本有贴图的 都设置成基础材质
            if ((mesh.material as MeshStandardMaterial).map) {
              (mesh.material as MeshStandardMaterial).map!.colorSpace = SRGBColorSpace;
              mesh.material = new MeshBasicMaterial({
                transparent: mesh.material.transparent,
                opacity: mesh.material.opacity,
                alphaTest: mesh.material.alphaTest,
                color: /^cloth(\d+)?$/.test(mesh.name) ? clothColor : '#ffffff',
                side: mesh.material.side,
                map: (mesh.material as MeshStandardMaterial).map,
              });
            } else {
              if (goodsData && [0].includes(goodsData.fillType)) {
                mesh.material = new MeshStandardMaterial({
                  normalMap: (mesh.material as MeshStandardMaterial).normalMap,
                  transparent: true,
                  opacity: mesh.material.opacity,
                  alphaTest: 0.1,
                  color: '#ffffff',
                  metalness: 0,
                  roughness: 0.8,
                  side: mesh.material.side,
                  map: textureManager.getTexture(mesh.name.replace(/\d+$/, '')) || null,
                });

                // 上身效果也需要添加一个背面模型
                const backMesh = mesh.clone();
                backMesh.material = backMesh.material.clone();
                backMesh.material.side = BackSide;
                (backMesh.material as MeshStandardMaterial).map = null;
                backMeshs.push(backMesh);
              } else {
                mesh.material = new MeshBasicMaterial({
                  transparent: true,
                  opacity: mesh.material.opacity,
                  alphaTest: mesh.material.alphaTest,
                  color: '#ffffff',
                  side: FrontSide,
                  map: textureManager.getTransparentTexture(mesh.name.replace(/\d+$/, '')) || null,
                });
              }
            }
          });

          backMeshs.map((o) => {
            mockupMesh.add(o);
          });

          this.renderMockups(mockup);

          resolve();
        });
      });
    } else {
      this.renderMockups(mockup);
    }
  }

  // 渲染上身效果
  async renderMockups(mockup: string) {
    if (!mockup) {
      return;
    }

    this.needRender = true;

    this.mockupMeshGroup.children.map((obj) => {
      obj.visible = obj.name === mockup;
    });

    this.renderer.setRenderTarget(this.baseRenderTarget);
    this.renderer.clear();
    this.renderer.render(this.scene, this.getCamera());
    this.renderer.setRenderTarget(null);
    this.renderer.clear();
    this.renderRendererTarget(this.baseRenderTarget);

    this.canvasMap
      .filter((c) => c.url === mockup)
      .map((c) => {
        if (c.canvas) {
          const ctx = c.canvas.getContext('2d');
          if (ctx) {
            ctx.imageSmoothingQuality = 'high';
            ctx.imageSmoothingEnabled = true;
            ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
            ctx.drawImage(this.renderer.domElement, 0, 0, ctx.canvas.width, ctx.canvas.height);
          }
        }
      });
  }

  //监听设计数据发生改变就修改上身效果的衣服颜色
  dispose1 = autorun(
    () => {
      const { design_data, needUpdateTime } = editorStore;
      if (needUpdateTime > 0) {
        const { colorValue } = design_data;
        this.mockupMeshGroup.children.map((mockupMesh) => {
          mockupMesh.traverse((mesh) => {
            if (/^cloth(\d+)?$/.test(mesh.name)) {
              // 颜色整体提亮避免黑色时看不到细节
              const colorObject = new Color(colorValue);
              const max = Math.max(...colorObject.toArray());
              const colorInterval = 1 - max;
              colorObject.r += colorInterval * 0.01;
              colorObject.g += colorInterval * 0.01;
              colorObject.b += colorInterval * 0.01;
              ((mesh as Mesh).material as MeshBasicMaterial).color.copy(colorObject);
            }
          });
        });
      }
    },
    { delay: 30 },
  );

  // 获取前面的缩略图
  async getThumbFile() {
    await this.resetLookAt({
      center: new Vector3(0, 0, 0),
      position: new Vector3(0, 0, 10),
    });
    let blob = new Blob();

    while (blob.size < 5000) {
      this.renderer.clear();
      this.mockupMeshGroup.children.map((obj, i) => {
        obj.visible = i === 0;
      });
      
      this.renderer.setRenderTarget(this.baseRenderTarget);
      this.renderer.clear();
      this.renderer.render(this.scene, this.getCamera());
      this.renderer.setRenderTarget(null);
      this.renderer.clear();
      this.renderRendererTarget(this.baseRenderTarget);

      await new Promise((resolve) => requestAnimationFrame(resolve));
      blob = await new Promise<Blob>((resolve, reject) => {
        this.renderer.domElement.toBlob(
          (blob) => {
            resolve(blob as Blob);
          },
          'image/png',
          1,
        );
      });
    }

    const file = new File([blob], 'test.png', { type: blob.type });
    return file;
  }

  //获取后面的缩率图
  async getBackThumbFile() {
    let blob = new Blob();

    while (blob.size < 5000) {
      this.renderer.clear();
      this.mockupMeshGroup.children.map((obj, i) => {
        obj.visible = i === Math.min(1, this.mockupMeshGroup.children.length - 1);
      });
      this.renderer.setRenderTarget(this.baseRenderTarget);
      this.renderer.clear();
      this.renderer.render(this.scene, this.getCamera());
      this.renderer.setRenderTarget(null);
      this.renderer.clear();
      this.renderRendererTarget(this.baseRenderTarget);
      await new Promise((resolve) => requestAnimationFrame(resolve));
      blob = await new Promise<Blob>((resolve, reject) => {
        this.renderer.domElement.toBlob(
          (blob) => {
            resolve(blob as Blob);
          },
          'image/png',
          1,
        );
      });
    }

    await this.resetLookAt({
      center: new Vector3(0, 0, 0),
      position: new Vector3(0, 0, 10),
    });

    const file = new File([blob], 'test.png', { type: blob.type });
    return file;
  }

  //获取全部效果图
  async getAllthumbFile() {
    const promises: Array<Promise<File>> = [];

    for (let { url, canvas } of this.canvasMap) {
      promises.push(
        new Promise<File>(async (resolve, reject) => {
          const blob = await new Promise<Blob>((resolve, reject) => {
            canvas.toBlob((blob) => resolve(blob as Blob), 'image/png', 1);
          });
          const file = new File([blob], 'test.png', { type: blob.type });
          resolve(file);
        }),
      );
    }

    const files = await Promise.all(promises);

    return files;
  }
}

const preview2DRenderer = new Preview2DRenderer();

export default preview2DRenderer;
